﻿
import React, { Component } from 'react';
import {
    View,
    Text,
    RefreshControl,
    PanResponder,
    Animated,
    Easing,
    Dimensions,
    ActivityIndicator,
    StyleSheet,
    Platform
} from 'react-native';
// const padding = 2; //scrollview与外面容器的距离
const pullOkMargin = 100; //下拉到ok状态时topindicator距离顶部的距离
const defaultDuration = 300;
const defaultTopIndicatorHeight = 80; //顶部刷新指示器的高度
const defaultFlag = {pulling: false, pullok: false, pullrelease: false};
const flagPulling = {pulling: true, pullok: false, pullrelease: false};
const flagPullok = {pulling: false, pullok: true, pullrelease: false};
const flagPullrelease = {pulling: false, pullok: false, pullrelease: true};
const isDownGesture = (x, y) => {
    return y > 0 && (y > Math.abs(x));
};
const isUpGesture = (x, y) => {
    return y < 0 && (Math.abs(x) < Math.abs(y));
};
const isVerticalGesture = (x, y) => {
    return (Math.abs(x) < Math.abs(y));
};


export default class extends Component {

    constructor(props) {
        super(props);
        this.pullable = this.props.refreshControl == null;
        this.defaultScrollEnabled = false;  //定义onPull***属性时scrollEnabled为false
        this.topIndicatorHeight = this.props.topIndicatorHeight ? this.props.topIndicatorHeight : defaultTopIndicatorHeight;
        this.defaultXY = {x: 0, y: this.topIndicatorHeight * -1};
        this.pullOkMargin = this.props.pullOkMargin ? this.props.pullOkMargin : pullOkMargin;
        this.duration = this.props.duration ? this.props.duration : defaultDuration;
        this.BaseState = Object.assign({}, props, {
            arrowAngle: new Animated.Value(0),
            pullPan: new Animated.ValueXY(this.defaultXY),
            scrollEnabled: this.defaultScrollEnabled,
            flag: defaultFlag,
            height: 0,
            prArrowDeg:new Animated.Value(0),
        });
        // this.scrollEnabled = this.defaultScrollEnabled;
        this.gesturePosition = {x: 0, y: 0};
        this.onScroll = this.onScroll.bind(this);
        this.onLayout = this.onLayout.bind(this);
        this.BeginRefresh = this.BeginRefresh.bind(this);
        this.StopRefresh = this.StopRefresh.bind(this);
        this.isPullState = this.isPullState.bind(this);
        this.resetDefaultXYHandler = this.resetDefaultXYHandler.bind(this);
        this.resolveHandler = this.resolveHandler.bind(this);
        this.setFlag = this.setFlag.bind(this);
        this.renderTopIndicator = this.renderTopIndicator.bind(this);
        this.renderSpinner = this.renderSpinner.bind(this);
        this.defaultTopIndicatorRender = this.defaultTopIndicatorRender.bind(this);
        this.panResponder = PanResponder.create({
            onStartShouldSetPanResponder: this.onShouldSetPanResponder.bind(this),
            onMoveShouldSetPanResponder: this.onShouldSetPanResponder.bind(this),
            onPanResponderGrant: () => {},
            onPanResponderMove: this.onPanResponderMove.bind(this),
            onPanResponderRelease: this.onPanResponderRelease.bind(this),
            onPanResponderTerminate: this.onPanResponderRelease.bind(this),
        });
        this.IOS = (Platform.OS==='ios'?true:false);
        this.flag = defaultFlag;
        this.storyTimeKey = "story_time_key";
        this.base64Icon = '';
    }

    onShouldSetPanResponder(e, gesture) {
        if(this.type==='View'){
                if (this.IOS&&(!this.pullable || isUpGesture(gesture.dx, gesture.dy)||!isVerticalGesture(gesture.dx, gesture.dy))) { //不使用pullable,或非向上 或向下手势不响应
                    return false;
                }else{
                    if (!this.IOS&&(!this.pullable || !isVerticalGesture(gesture.dx, gesture.dy))) { //不使用pullable,或非向上 或向下手势不响应
                        return false;
                    }
                }
        }else{
            if (!this.pullable || !isVerticalGesture(gesture.dx, gesture.dy)) { //不使用pullable,或非向上 或向下手势不响应
                return false;
            }
        }

        if (!this.state.scrollEnabled) {
            this.lastY = this.state.pullPan.y._value;
            return true;
        } else {
            return false;
        }
    }

    BeginRefresh(){
        Log('BeginRefresh');
        this.setFlag(flagPullrelease);
        this.state.pullPan.setValue({x: this.defaultXY.x, y: this.topIndicatorHeight});

    }

    StopRefresh(){
        Log('StopRefresh');
        this.resetDefaultXYHandler();
    }

    onPanResponderMove(e, gesture) {
        this.gesturePosition = {x: this.defaultXY.x, y: gesture.dy};
        if (isUpGesture(gesture.dx, gesture.dy)) { //向上滑动
            if(this.isPullState()) {
                this.resetDefaultXYHandler();
            } else if(this.props.onPushing && this.props.onPushing(this.gesturePosition)) {
                // do nothing, handling by this.props.onPushing
            } else {
                if(this.type==='View'){
                    this.scroll&&this.scroll.scrollTo({x:0, y: gesture.dy * -1,animated:true});
                }else{
                    this.scroll&&this.scroll.scrollToOffset({animated: true,
                        offset: gesture.dy * -1});
                }
            }
            return;
        } else if (isDownGesture(gesture.dx, gesture.dy)) { //下拉
            this.state.pullPan.setValue({x: this.defaultXY.x, y: this.lastY + gesture.dy / 2});
            if (gesture.dy < this.topIndicatorHeight + this.pullOkMargin) { //正在下拉
                if (!this.flag.pulling) {
                    this.props.onPulling && this.props.onPulling();
                }
                this.setFlag(flagPulling);
            } else { //下拉到位
                if (!this.state.pullok) {
                    this.props.onPullOk && this.props.onPullOk();
                }
                this.setFlag(flagPullok);
            }
        }
    }

    onPanResponderRelease(e, gesture) {
        if (this.flag.pulling) { //没有下拉到位
            this.resetDefaultXYHandler(); //重置状态
        }
        if (this.flag.pullok) {
            if (!this.flag.pullrelease) {
                if (this.props.onPullRelease) {
                    this.props.onPullRelease(this.resolveHandler);
                } else {
                    setTimeout(() => {this.resetDefaultXYHandler()}, 3000);
                }
            }
            this.setFlag(flagPullrelease); //完成下拉，已松开
            Animated.timing(this.state.pullPan, {
                toValue: {x: 0, y: 0},
                easing: Easing.linear,
                duration: this.duration
            }).start();
        }
    }

    onScroll(e) {
        if (e.nativeEvent.contentOffset.y <= 0) {
            // Log('onScroll e.nativeEvent.contentOffset.y',e.nativeEvent.contentOffset.y);
            // this.scrollEnabled = this.defaultScrollEnabled;
            this.state.scrollEnabled===this.defaultScrollEnabled?"":this.setState({scrollEnabled: this.defaultScrollEnabled});
        } else if(!this.isPullState()) {
            // Log('onScroll e.nativeEvent.contentOffset.y',e.nativeEvent.contentOffset.y);
            // this.scrollEnabled = true;
            this.state.scrollEnabled?"":this.setState({scrollEnabled: true});
        }
    }

    isPullState() {
        return this.flag.pulling || this.flag.pullok || this.flag.pullrelease;
    }

    setFlag(flag) {
        if (this.flag != flag) {
            this.flag = flag;
            Log('设置inderTop',this.flag);
            this.renderTopIndicator();
        }
    }

    /** 数据加载完成后调用此方法进行重置归位
     */
    resolveHandler() {
        if (this.flag.pullrelease) { //仅触摸松开时才触发
            this.resetDefaultXYHandler();
        }
    }

    resetDefaultXYHandler() {
        this.flag = defaultFlag;
        Animated.timing(this.state.pullPan, {
            toValue: {x: 0, y: this.topIndicatorHeight * -1},
            easing: Easing.linear,
            duration: this.duration
        }).start();
    }

    componentWillUpdate(nextProps, nextState) {
        if (nextProps.isPullEnd && this.state.pullrelease) {
            this.resetDefaultXYHandler();
        }
    }

    onLayout(e) {
        if (this.state.width != e.nativeEvent.layout.width || this.state.height != e.nativeEvent.layout.height) {
            this.scrollContainer.setNativeProps({style: {width: e.nativeEvent.layout.width, height: e.nativeEvent.layout.height}});
        }
    }

    render() {
       // let refreshControl = this.props.refreshControl;
        return (
            <View style={[styles.wrap, this.props.style]} onLayout={this.onLayout}>
                <Animated.View ref={(c) => {this.ani = c;}}
                               style={[this.state.pullPan.getLayout()]}>
                    {this.renderTopIndicator()}
                    <View ref={(c) => {this.scrollContainer = c;}}
                          {...this.panResponder.panHandlers}
                          style={{width: this.state.width, height: this.state.height}}>
                        {this.getScrollable()}
                    </View>
                </Animated.View>
            </View>
        );
    }

    renderTopIndicator() {
        let { pulling, pullok, pullrelease } = this.flag;
        if (this.props.topIndicatorRender == null) {
            return this.defaultTopIndicatorRender(pulling, pullok, pullrelease, this.gesturePosition);
        } else {
            return this.props.topIndicatorRender(pulling, pullok, pullrelease, this.gesturePosition);
        }
    }

    /**
     使用setNativeProps解决卡顿问题
     make changes directly to a component without using state/props to trigger a re-render of the entire subtree
     */
    // defaultTopIndicatorRender(pulling, pullok, pullrelease, gesturePosition) {
    //
    //     this.transform = [{rotate:this.state.prArrowDeg.interpolate({
    //         inputRange: [0,1],
    //         outputRange: ['0deg', '-180deg']
    //     })}];
    //
    //     if (pulling) {
    //         Animated.timing(this.state.prArrowDeg, {
    //             toValue: 0,
    //             duration: 100,
    //             easing: Easing.inOut(Easing.quad)
    //         }).start();
    //         this.txtPulling && this.txtPulling.setNativeProps({style: styles.show});
    //         this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
    //         this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
    //     } else if (pullok) {
    //         Animated.timing(this.state.prArrowDeg, {
    //             toValue: 1,
    //             duration: 100,
    //             easing: Easing.inOut(Easing.quad)
    //         }).start();
    //         this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
    //         this.txtPullok && this.txtPullok.setNativeProps({style: styles.show});
    //         this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
    //     } else if (pullrelease) {
    //         this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
    //         this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
    //         this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.show});
    //     }
    //     return (
    //         <View style={styles.headWrap}>
    //             <View ref={(c) => {this.txtPulling = c;}} style={styles.hide}>
    //                 <Animated.Image style={[styles.arrow,{transform:this.transform}]}
    //                                 resizeMode={'contain'}
    //                                 source={{uri: this.base64Icon}}/>
    //                 <Text style={styles.arrowText}>{"下拉可以刷新"}</Text>
    //             </View>
    //
    //             <View ref={(c) => {this.txtPullok = c;}} style={styles.hide}>
    //
    //                 <Animated.Image style={[styles.arrow,{transform:this.transform}]}
    //                                 resizeMode={'contain'}
    //                                 source={{uri: this.base64Icon}}/>
    //                 <Text style={styles.arrowText}>{"释放立即刷新"}</Text>
    //             </View>
    //
    //             <View ref={(c) => {this.txtPullrelease = c;}} style={styles.hide}>
    //                 <ActivityIndicator size="small" color="gray" style={styles.arrow}/>
    //                 <Text style={styles.arrowText}>{"刷新数据中..."}</Text>
    //             </View>
    //
    //             <Text>下拉刷新时间:  {dateFormat(new Date().getTime(),'yyyy-MM-dd hh:mm')}</Text>
    //         </View>
    //     );
    // }

    renderSpinner(status) {
        if (status === "txtPullrelease") {
            return (
                <ActivityIndicator style={{marginRight: 10}}/>
            )
        }
        return (
            <Animated.Image
                source={{uri:this.base64Icon}}
                resizeMode={'contain'}
                style={[defaultHeaderStyles.arrow,
                    this.props.arrowImageStyle,
                    {
                        transform: [{
                            rotateX: this.state.arrowAngle.interpolate({
                                inputRange: [0, 1],
                                outputRange: ['0deg', '-180deg']
                            })
                        }]
                    }]}
            />
        )
    }

    defaultTopIndicatorRender(pulling, pullok, pullrelease, gesturePosition) {
        Log('pulling, pullok, pullrelease',pulling, pullok, pullrelease);
        if (pulling) {
            Animated.timing(this.state.arrowAngle, {
                toValue: 0,
                duration: 50,
                easing: Easing.inOut(Easing.quad)
            }).start();
            this.txtPulling && this.txtPulling.setNativeProps({style: styles.show});
            this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
            this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
        } else if (pullok) {
            Animated.timing(this.state.arrowAngle, {
                toValue: 1,
                duration: 50,
                easing: Easing.inOut(Easing.quad)
            }).start();
            this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
            this.txtPullok && this.txtPullok.setNativeProps({style: styles.show});
            this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.hide});
        } else if (pullrelease) {
            Animated.timing(this.state.arrowAngle, {
                toValue: 1,
                duration: 50,
                easing: Easing.inOut(Easing.quad)
            }).start();
            this.txtPulling && this.txtPulling.setNativeProps({style: styles.hide});
            this.txtPullok && this.txtPullok.setNativeProps({style: styles.hide});
            this.txtPullrelease && this.txtPullrelease.setNativeProps({style: styles.show});
        }
        return (
            <View ref={(c) => {this.PullAll = c;}} style={defaultHeaderStyles.header}>
                <View ref={(c) => {this.txtPulling = c;}} style={styles.hide}>
                    {this.renderSpinner("txtPulling")}
                    <Text style={styles.arrowText}>{"下拉可以刷新"}</Text>
                </View>
                <View ref={(c) => {this.txtPullok = c;}} style={styles.hide}>
                    {this.renderSpinner("txtPullok")}
                    <Text style={styles.arrowText}>{"释放立即刷新"}</Text>
                </View>
                <View ref={(c) => {this.txtPullrelease = c;}} style={styles.hide}>
                    {this.renderSpinner("txtPullrelease")}
                    <Text style={styles.arrowText}>{"刷新数据中..."}</Text>
                </View>
            </View>
        );
    }
}

const dateFormat = function(dateTime, fmt) {
    let date = new Date(dateTime);
    fmt = fmt || 'yyyy-MM-dd';
    let o = {
        "M+": date.getMonth() + 1, //月份
        "d+": date.getDate(), //日
        "h+": date.getHours(), //小时
        "m+": date.getMinutes(), //分
        "s+": date.getSeconds(), //秒
        "q+": Math.floor((date.getMonth() + 3) / 3), //季度
        "S": date.getMilliseconds() //毫秒
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (let k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}



const styles = StyleSheet.create({
    wrap:{
        flex: 1,
    },
    headWrap:{
        height: defaultTopIndicatorHeight,
        justifyContent: 'center',
        alignItems: 'center',
    },
    hide: {
        position: 'absolute',
        left: 10000
    },
    show:{
        position: 'relative',
        left: 0,
        flexDirection: 'row',
        width: Dimensions.get('window').width,
        height: 40,
        justifyContent: 'center',
        alignItems: 'center',
    },
    arrow:{
        height: 30,
        width: 30,
        marginRight: 20,
    },
    arrowText:{
        marginLeft: 20,
    }
});
    const defaultHeaderStyles = StyleSheet.create({
        header: {
        height: 80,
        alignItems: 'center',
        justifyContent: 'center'
    },
        status: {
        flexDirection: 'row',
        alignItems: 'center'
    },
        arrow: {
        width: 23,
        height: 23,
        marginRight: 10,
        opacity: 0.4
    },
        statusTitle: {
        fontSize: 13,
        color: '#333333'
    },
        date: {
        fontSize: 11,
        color: '#333333',
        marginTop: 5
    }
    });